//
//  bcDiscriminatorAppController.m
//  bcDiscriminatorApplicationController
//
//  Created by Volker Runkel on 03.11.06.
//  Copyright 2006 Volker Runkel, ecoObs.
//	Mail me at runkel @ eco obs . de
//
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#import "bcDiscriminatorAppController.h"

@implementation bcDiscriminatorAppController

- (NSManagedObjectModel *)managedObjectModel {
    if (managedObjectModel) return managedObjectModel;
	
	NSMutableSet *allBundles = [[NSMutableSet alloc] init];
	[allBundles addObject: [NSBundle mainBundle]];
	[allBundles addObjectsFromArray: [NSBundle allFrameworks]];
    
    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles: [allBundles allObjects]] retain];
    [allBundles release];
    return managedObjectModel;
}

- (NSManagedObjectContext *) managedObjectContext {
    
    if (managedObjectContext) {
        return managedObjectContext;
    }

	NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    
    return managedObjectContext;
}

- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
	
    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
#define error error
    [persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:nil];
#define error Rf_error
    return persistentStoreCoordinator;
}

- (void)awakeFromNib
{
	inputAvailable = NO;
	manualPrediction = NO;
	canceled = NO;
	[self performSelector:@selector(awakeREngine) withObject:nil afterDelay:0.1];

	
	availableDiscrObjects = [[NSDictionary alloc] 
			initWithObjects:[NSArray arrayWithObjects:@"genrf",@"myorf",@"piprf",@"nycrf",@"myokmrf",@"piptiefrf",@"nycmirf",nil] 
			forKeys:[NSArray arrayWithObjects:@"Spec.",@"Myotis",@"Pipistrelloid",@"Nyctaloid",@"Mkm",@"Ptief",@"Nycmi",nil]];
	
	availableOutlierObjects = [[NSDictionary alloc] 
			initWithObjects:[NSArray arrayWithObjects:@"S1myoOutlier",@"S1pipOutlier",@"S1nycOutlier",@"S1barOutlier",@"S1plecOutlier",@"S2malcOutlier",@"S2mkmOutlier",
				@"S2mmyoOutlier", @"S2mnatOutlier",@"S2ppipOutlier",@"S2ppygOutlier", @"S2ptiefOutlier", @"S3hsavOutlier", @"S3mbartOutlier", @"S3mbecOutlier"
				, @"S3mdauOutlier", @"S3pkuhOutlier", @"S3pnatOutlier", @"S2enilOutlier", @"S2nnocOutlier", @"S2nycmiOutlier", @"S3nleiOutlier", @"S3vmurOutlier", @"S3eserOutlier", nil] 
					forKeys:[NSArray arrayWithObjects:@"Myotis",@"Pipistrelloid",@"Nyctaloid",@"Bbar",@"Plecotus",@"Malc",@"Mkm", @"Mmyo", 
				@"Mnat", @"Ppip", @"Ppyg", @"Ptief",@"Hsav", @"Mbart", @"Mbec", @"Mdau", @"Pkuh", @"Pnat",@"Enil",@"Nnoc",@"Nycmi", @"Nlei", @"Vmur", @"Eser", nil]];
	
	availableOutlierProbs = [[NSDictionary alloc] 
			initWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.90]/*Myotis*/,[NSNumber numberWithFloat:0.95]/*Pipistr*/,[NSNumber numberWithFloat:0.95]/*Nyctaloid*/,[NSNumber numberWithFloat:0.85]/*Bbar*/,[NSNumber numberWithFloat:0.80]/*Plecotus*/,[NSNumber numberWithFloat:0.90]/*Malc*/,[NSNumber numberWithFloat:0.85]/*Mkm*/,
				[NSNumber numberWithFloat:0.90]/*Mmyo*/, [NSNumber numberWithFloat:0.90]/*Mnat*/,[NSNumber numberWithFloat:0.90]/*Ppip*/,[NSNumber numberWithFloat:0.95]/*Ppyg*/, [NSNumber numberWithFloat:0.90]/*Ptief*/, [NSNumber numberWithFloat:0.95]/*Hsav*/, [NSNumber numberWithFloat:0.85]/*Mbart*/, [NSNumber numberWithFloat:0.82]/*Mbec*/
				, [NSNumber numberWithFloat:0.90]/*Mdau*/, [NSNumber numberWithFloat:0.85]/*Pkuh*/, [NSNumber numberWithFloat:0.90]/*Pnat*/, [NSNumber numberWithFloat:0.85]/*Enil*/, [NSNumber numberWithFloat:0.80]/*Nnoc*/, [NSNumber numberWithFloat:0.90]/*Nycmi*/, [NSNumber numberWithFloat:0.75]/*Nlei*/, [NSNumber numberWithFloat:0.82]/*Vmur*/, [NSNumber numberWithFloat:0.85]/*Eser*/, nil] 
					forKeys:[NSArray arrayWithObjects:@"Myotis",@"Pipistrelloid",@"Nyctaloid",@"Bbar",@"Plecotus",@"Malc",@"Mkm", @"Mmyo", 
						@"Mnat", @"Ppip", @"Ppyg", @"Ptief",@"Hsav", @"Mbart", @"Mbec", @"Mdau", @"Pkuh", @"Pnat",@"Enil",@"Nnoc",@"Nycmi", @"Nlei", @"Vmur", @"Eser", nil]];
	
	
	stepAM = [[NSMutableDictionary alloc] init];
	
	[progressBar setUsesThreadedAnimation:YES];
	
	if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:@"decimalSep"])
		[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:0] forKey:@"decimalSep"];

	if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:@"mainMinCalls"])
		[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:3] forKey:@"mainMinCalls"];
	
	if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:@"mainMinProb"])
		[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:0.6] forKey:@"mainMinProb"];

	if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:@"fixedOutlierProb"])
		[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"fixedOutlierProb"];

	if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:@"fixedOutlierProbValue"])
		[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:0.90] forKey:@"fixedOutlierProbValue"];

	if (nil == [[NSUserDefaults standardUserDefaults] objectForKey:@"useNewAveraging"])
		[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"useNewAveraging"];

}

- (void)dealloc
{
	[probsArray release], probsArray = nil;
	[classArray release], classArray=nil;
	[lastCallNumbers release], lastCallNumbers=nil;
	[results release], results=nil;
	[resultProbs release], resultProbs=nil;
	[availableDiscrObjects release], availableDiscrObjects = nil;
	[toDoList release], toDoList = nil;
	[discObject release], discObject = nil;
	[maxClass release], maxClass = nil;
	[stepAM release], stepAM = nil;
	[lastClass release], lastClass = nil;
	[winList release], winList = nil;
	[seconds release], seconds=nil;
	
	[super dealloc];
	
}

- (NSArray*)discrObjects
{
	return [availableDiscrObjects allValues];
}

- (NSArray*)outlObjects
{
	return [availableOutlierObjects allValues];
}

- (void)awakeREngine
{
	[BCREWrapper Rwrapper];
	[chooseFilesButton setEnabled:YES];
	[startPredButton setEnabled:YES];
}

- (void)writeToConsole:(NSString*)text
{
	[consoleView setEditable:YES];
	[consoleView insertText:text];
	[consoleView setEditable:NO];
}

- (void)setRVersion:(NSString*)text
{
	[rversion setEditable:YES];
	[rversion setStringValue:text];
	[rversion setEditable:NO];
}

- (NSString*)currentAnalyseLevel
{
	if (!manualPrediction) return discObject;
	else return @"Genus";
}

- (NSArray*)buildFilesFromDeepTraversal:(NSString*)startFolder
{
	NSMutableArray *tempArray = [[[NSMutableArray alloc] init] autorelease];
	NSString *file;
	NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:startFolder];
	
	while (file = [dirEnum nextObject]) {
		if ([[file pathExtension] isEqualToString: @"csv"]) {
			[tempArray addObject:[startFolder stringByAppendingPathComponent:file]];
		}
	}
	
	return tempArray;
	
}

- (IBAction)chooseInputFile:(id)sender
{
	NSOpenPanel *op = [NSOpenPanel openPanel];
	int result;
	
	[op setCanChooseFiles:YES];
	[op setCanChooseDirectories:NO];
	[op setAllowsMultipleSelection:YES];
	[op setAllowedFileTypes:[NSArray arrayWithObject:@"csv"]];
	[op setAccessoryView:fileChooseAccView];
	[op setDelegate:self];
	result = [op runModalForDirectory:nil file:nil types:[NSArray arrayWithObject:@"csv"]];
	if (result == NSOKButton) {

		[chooseFilesButton setEnabled:NO];
		[startPredButton setEnabled:NO];

		if ([[op filenames] count] > 0 ) {
			int i, j;

			// we decide to deep traverse if a single directory was chosen
			// so we need to check for that and build an inputFiles array from the chosen path
			BOOL isDir = NO;
			NSArray *inputFiles;
			if ([[op filenames] count] == 1 && [[NSFileManager defaultManager] fileExistsAtPath:[[op filenames] objectAtIndex:0] isDirectory:&isDir] && isDir )
				inputFiles = [[self buildFilesFromDeepTraversal:[[op filenames] objectAtIndex:0]] copy];
			else inputFiles = [[op filenames] copy];
			
			//NSLog(@"%@", inputFiles);
			//return;
			
			if ( [inputFiles count] > 1000 ) {
				if (NSRunAlertPanel(@"Proceed with discrimination?",@"You have selected %d files for discrimination. Do you really want to proceed with that many files?",@"Yes",@"No",nil, [inputFiles count]) != NSOKButton) {
					[chooseFilesButton setEnabled:YES];
					[startPredButton setEnabled:YES];
					return;
				}
			}
			
			NSDate *startedAt = [NSDate date];
			[progressBar setMaxValue:[inputFiles count]];
			[progressBar setDoubleValue:0.0];
			[progressBar startAnimation:self];
			[cancelButton setHidden:NO];
			
			for (i=0;i<[inputFiles count];i++) {

				[progressBar setDoubleValue:i];
				
				newFile = YES;

				[inputFile setStringValue:[inputFiles objectAtIndex:i]];
				[[BCREWrapper Rwrapper] setFileName:[inputFiles objectAtIndex:i]];
				inputAvailable = YES;
				[self startPrediction:self];
				// write results file
				NSString *outfile = [NSString stringWithFormat:@"%@.res",[[inputFiles objectAtIndex:i] stringByDeletingPathExtension]];
				NSMutableString *resultOutput = [[NSMutableString alloc] init];
				
				[self evalOutputResults];
				for (j=0;j<[seconds count];j++) {
					[resultOutput appendString:[seconds objectAtIndex:j]];
				}
				
				//NSLog(resultOutput);
				
				if (NSOffState == [omitResFiles state] )
					[resultOutput writeToFile:outfile atomically:YES];
				[self writeToConsole:resultOutput];
				[self writeToConsole:@"\n"];

				[resultOutput release];
				if (canceled) break;
			}
			canceled = NO;
			[progressBar stopAnimation:self];
			[self writeToConsole:[NSString stringWithFormat:@"elapsed time per recording %f",-1.0*[startedAt timeIntervalSinceNow]/(float)[inputFiles count]]];
		}
		[chooseFilesButton setEnabled:YES];
		[startPredButton setEnabled:YES];
	}
}

- (IBAction)startPrediction:(id)sender
{
	if (inputAvailable) {
		[discObject release];
		discObject = @"genrf";
		lastClass = @"NA";
		lastAvg = 0.0;
		firstResultEntry = NO;
		tooFewCalls = NO;
		[winList release];
		winList = [[NSMutableArray alloc] init];
		[seconds release];
		seconds = [[NSMutableArray alloc] init];
		
		NSSet *allCalls = [managedObjectContext registeredObjects];
		NSEnumerator *e = [allCalls objectEnumerator];
		NSManagedObject *oToE;
		while (oToE = [e nextObject]) [managedObjectContext deleteObject:oToE];
		[managedObjectContext processPendingChanges];
		
		[outputResults release];
		outputResults = [[NSMutableArray alloc] init];
		[outputResults addObject:[[NSMutableArray alloc] init]];
		[outputResults addObject:[[NSMutableArray alloc] init]];
		[outputResults addObject:[[NSMutableArray alloc] init]];
		[outputResults addObject:[[NSMutableArray alloc] init]];
		[outputResults addObject:[[NSMutableArray alloc] init]];
		[outputResults addObject:[[NSMutableArray alloc] init]];
		
		if ([[BCREWrapper Rwrapper] predictFile:nil]) {
			
			mainGenusLevel = 0;
			
			[toDoList release];
			toDoList = [[NSMutableArray alloc] init];
			
			[results release];
			results = [[NSMutableArray alloc] init];
			[resultProbs release];
			resultProbs = [[NSMutableArray alloc] init];
			
			[lastCallNumbers release];
			lastCallNumbers = [[NSMutableArray alloc] init];
			
			// ProbsArray is filled from the PredictionEngine
			// We do fill the resultsArray with standard info, 
			// before looking at the real results
			// For the rewrite we added outputResults here as well
			int i, j;
			float max;
			for (i=0;i<[[probsArray objectAtIndex:1] intValue];i++) {
				[results addObject:@"Spec."];
				[resultProbs addObject:[NSNumber numberWithFloat:0.0]];
				[lastCallNumbers addObject:[NSNumber numberWithInt:i+1]];

				[[outputResults objectAtIndex:0] addObject:@"NA"]; // the last
				[[outputResults objectAtIndex:1] addObject:[NSNumber numberWithFloat:0.0]]; // the "probs" of last
				[[outputResults objectAtIndex:4] addObject:@"NA"]; // the last
				[[outputResults objectAtIndex:5] addObject:[NSNumber numberWithFloat:0.0]]; // the "probs" of last
				
				max = [[probsArray objectAtIndex:(i*[[probsArray objectAtIndex:0] intValue])+2] floatValue];
				[[outputResults objectAtIndex:2] addObject:[classArray objectAtIndex:0]];
				
				for (j=1; j<[[probsArray objectAtIndex:0] intValue]; j++) {
					if (max<[[probsArray objectAtIndex:(i*[[probsArray objectAtIndex:0] intValue])+j+2] floatValue]) {
						max = [[probsArray objectAtIndex:(i*[[probsArray objectAtIndex:0] intValue])+j+2] floatValue];
						[[outputResults objectAtIndex:2] removeLastObject];
						[[outputResults objectAtIndex:2] addObject:[classArray objectAtIndex:j]];		
					}
				}
				[[outputResults objectAtIndex:3] addObject:[NSNumber numberWithFloat:max]]; // the "probs" of species
			}
				
			[self updateResultsTable];

			[self checkToDo];
		}
	}
	else [self writeToConsole:@"No input file choosen or other problems occurred!"];
}

- (IBAction)cancelPrediction:(id)sender
{
	canceled = YES;
	[cancelButton setHidden:YES];
}

- (void)setProbsArray:(NSMutableArray*)newArray
{
	[probsArray removeAllObjects];
	[probsArray release];
	probsArray = [newArray mutableCopy];
}

- (void)setClassArray:(NSMutableArray*)newArray
{
	[classArray release];
	classArray = [newArray mutableCopy];
}

- (NSString*)currentOutlierLevel
{
	return outlObject;	
}

- (float)currentOutlObjectProb
{
	if ([[NSUserDefaults standardUserDefaults] boolForKey:@"fixedOutlierProb"]) return [[NSUserDefaults standardUserDefaults] floatForKey:@"fixedOutlierProbValue"];
	else return outlObjectProb;
}

- (void)updateResultsTable
{
	// fill resultController
	//int cols = [[probsArray objectAtIndex:0] intValue];
	//int rows = [[probsArray objectAtIndex:1] intValue];	
	//int size = cols*rows;
	//int i,j;

	[self evaluateAutomaticPrediction];
}

- (float)currRowMax:(int)currRow withCols:(int)cols andRows:(int)rows
{
	float max = 0.0;
	int i;
	
	for (i=0;i<cols;i++) {
		if (max < [[probsArray objectAtIndex:2+i+(currRow*cols)] floatValue]) max = [[probsArray objectAtIndex:2+i+(currRow*cols)] floatValue];
	}
	return max;	
}

- (int)currRowClass:(int)currRow withCols:(int)cols andRows:(int)rows
{
	float max = 0.0;
	int maxPos = 0;
	int i;
	for (i=0;i<cols;i++) {
		if (max < [[probsArray objectAtIndex:2+i+(currRow*cols)] floatValue]) {
			max = [[probsArray objectAtIndex:2+i+(currRow*cols)] floatValue];
			maxPos = i;
		}
	}

	return maxPos;
}

- (void)addToResultView:(NSString*)groupName withParent:(NSString*)parentName objectCount:(int)aNumber andAverage:(float)groupAverage
{
	NSManagedObject *newGroup = [[NSEntityDescription insertNewObjectForEntityForName:@"Groups" inManagedObjectContext:managedObjectContext] retain];
	[newGroup setValue:groupName forKey:@"name"];
	[newGroup setValue:[NSNumber numberWithInt:aNumber] forKey:@"callCount"];
	[newGroup setValue:[NSNumber numberWithFloat:groupAverage] forKey:@"probability"];
	
	NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Groups" inManagedObjectContext:managedObjectContext];
	NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
	[request setEntity:entityDescription];
	
	NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", parentName];
	[request setPredicate:predicate];
	
#define error error
	NSArray *array = [managedObjectContext executeFetchRequest:request error:nil];
	if (array == nil || [array count] < 1)
	{
		[newGroup setValue:nil forKeyPath:@"parent"];
	}
	else {
		[newGroup setValue:[array objectAtIndex:0] forKeyPath:@"parent"];
	}
#define error Rf_error
}

- (void)evaluateAutomaticPrediction
{
	int cols = [[probsArray objectAtIndex:0] intValue];
	int rows = [[probsArray objectAtIndex:1] intValue];
	int size = cols*rows + 1;
	int i,j, currow=0;
	int tempRows[rows];
		
	NSMutableArray *tempArray = [[NSMutableArray alloc] init];
	
	// Test for necessary updates due to Outlier detection
	// end new discrimination of 1st, 2nd, 3rd species
	// available classes are stored already in classArray
	// walk through last results and extract per class all calls (according to max)
	// and save them to a toDoList item each
	
	for (j=0;j<[classArray count];j++) {
		
		int ii;
		currow = 0;
		NSMutableArray *tempCallList = [[NSMutableArray alloc] init];
		for (ii=0; ii<rows; ii++) {
			if ([self currRowClass:ii withCols:cols andRows:rows] == j) {
				tempRows[currow] = ii;
				currow++;
				[tempCallList addObject:[lastCallNumbers objectAtIndex:ii]];
			}
		}
		
		if (nil!=[availableOutlierObjects objectForKey:[classArray objectAtIndex:j]] && [tempCallList count] > 0) {
			outlObject = [[availableOutlierObjects objectForKey:[classArray objectAtIndex:j]] copy];
			outlObjectProb = [[availableOutlierProbs objectForKey:[classArray objectAtIndex:j]] floatValue];
			//NSLog(@"%@ TempCallList before %@",[classArray objectAtIndex:j], tempCallList);
			tempCallList = [self updateOutputResultsWithOutlierData:[[BCREWrapper Rwrapper] outlierDetector:tempCallList] forCalls:tempCallList];
		}
		
		//NSLog(@"%@",outputResults); NSLog(@"=============\n");
		
		if ([tempCallList count] > 0) {
			
			int ii, jj = 0;
			float max;
			float groupAvg = 0.0, squ_avg = 0.0;
			int counter = 0, counter_old=0;
			
			if (![[NSUserDefaults standardUserDefaults] boolForKey:@"useNewAveraging"]) {
			
			for (ii=0;ii<[tempCallList count];ii++) {
				max = [self currRowMax:tempRows[jj++] withCols:cols andRows:rows];
				NSLog(@"max %f und %f" , max,[[[outputResults objectAtIndex:3] objectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1)] floatValue]);
				
				if ([[[outputResults objectAtIndex:3] objectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1)] floatValue] >= 0.5) {
					counter++;
					
					groupAvg += max;

					[[outputResults objectAtIndex:3] replaceObjectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1) withObject:[NSNumber numberWithFloat:max]];
					[[outputResults objectAtIndex:2] replaceObjectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1) withObject:[classArray objectAtIndex:j]];
					[resultProbs replaceObjectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1) withObject:[NSNumber numberWithFloat:max]];
				}
				else {
					[tempCallList removeObjectAtIndex:ii];
					ii--;
				}
			}
			
			groupAvg /= (float)counter;

			if ([tempCallList count] > 0)
				[self addToResultView:[classArray objectAtIndex:j] withParent:[[outputResults objectAtIndex:0] objectAtIndex:([[tempCallList objectAtIndex:0] intValue]-1)] objectCount:[tempCallList count] andAverage:groupAvg];
			
			if ([tempCallList count] > 0)
				[toDoList addObject:[NSDictionary dictionaryWithObject:[tempCallList copy] forKey:[classArray objectAtIndex:j]]];
			
			}
			
			else {
				
				for (ii=0;ii<[tempCallList count];ii++) {
					max = [self currRowMax:tempRows[jj++] withCols:cols andRows:rows];
					//NSLog(@"%f" , max);
					
					counter++;
						
					groupAvg += (max*max);
					[[outputResults objectAtIndex:3] replaceObjectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1) withObject:[NSNumber numberWithFloat:max]];
					[[outputResults objectAtIndex:2] replaceObjectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1) withObject:[classArray objectAtIndex:j]];
					[resultProbs replaceObjectAtIndex:([[tempCallList objectAtIndex:ii] intValue]-1) withObject:[NSNumber numberWithFloat:max]];
				}
				
				groupAvg = sqrtf ( (groupAvg / (float)counter) * log10f((float)counter+3.0));
				
				if ([tempCallList count] > 0)
					[self addToResultView:[classArray objectAtIndex:j] withParent:[[outputResults objectAtIndex:0] objectAtIndex:([[tempCallList objectAtIndex:0] intValue]-1)] objectCount:[tempCallList count] andAverage: ( (groupAvg > 1.0) ? 1.0 : groupAvg )];
				
				if ([tempCallList count] > 0)
					[toDoList addObject:[NSDictionary dictionaryWithObject:[tempCallList copy] forKey:[classArray objectAtIndex:j]]];
				
			}
		}
	}

	/* Removed June 3rd 08, when evaluating the Nyctaloid problem discovered by Uli
	   relevant code to fill resultProbs moved to above, filled with row maximum now, as before
	 for (j=0;j<cols;j++) {
		[tempArray removeAllObjects];
		currow = 0;
		for (i=2+j;i<=size;i=i+cols) {
			if ([[probsArray objectAtIndex:i] floatValue] >= [self currRowMax:currow withCols:cols andRows:rows] ) {
				[tempArray addObject:[[lastCallNumbers objectAtIndex:currow] copy]];
				[resultProbs replaceObjectAtIndex:[[lastCallNumbers objectAtIndex:currow] intValue]-1 withObject:[probsArray objectAtIndex:i]];
			}
			currow++;
		}
	}*/
	
	[tempArray release];
}

- (void)checkToDo
{
	int i = 0;
	int j;

	while (i < [toDoList count]) {
		//VRNSLog(@"Current ToDo %d: %@", i, [toDoList objectAtIndex:i]);
		
		int count = [[[toDoList objectAtIndex:i] objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]] count];

		for (j=0;j<count;j++) {
			[results replaceObjectAtIndex:[[[[toDoList objectAtIndex:i] objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]] objectAtIndex:j] intValue]-1 withObject:[[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0] copy]];
		}
		
		if (nil==[availableDiscrObjects objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]]) {
			[self writeToConsole:@"Found final results:\n"];
		}
		else {
			mainGenusLevel++;
			[lastCallNumbers release];
			lastCallNumbers = [[[toDoList objectAtIndex:i] objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]] copy];
			discObject = [[availableDiscrObjects objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]] copy];
			
			// Best position maybe here - at least test results are good now
			if (mainGenusLevel > 0 && nil!=[availableDiscrObjects objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]]) {
				int ii;
				for (ii=0;ii<[lastCallNumbers count];ii++) {
					[[outputResults objectAtIndex:4] replaceObjectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1) withObject:[[outputResults objectAtIndex:0] objectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1)]];
					[[outputResults objectAtIndex:5] replaceObjectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1) withObject:[[outputResults objectAtIndex:1] objectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1)]];
					[[outputResults objectAtIndex:0] replaceObjectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1) withObject:[[outputResults objectAtIndex:2] objectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1)]];
					[[outputResults objectAtIndex:1] replaceObjectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1) withObject:[[outputResults objectAtIndex:3] objectAtIndex:([[lastCallNumbers objectAtIndex:ii] intValue]-1)]];
				}
			}
			
			[[BCREWrapper Rwrapper] predictFile:[[toDoList objectAtIndex:i] objectForKey:[[[toDoList objectAtIndex:i] allKeys] objectAtIndex:0]]];
			
			[self updateResultsTable];
			
		}
		i++;
	}
	
	[self writeToConsole:@"\n"];
	[self writeToConsole:[results componentsJoinedByString:@";"]];
	[self writeToConsole:@"\n"];
	[self writeToConsole:[resultProbs componentsJoinedByString:@";"]];
	[self writeToConsole:@"\n"];
	//[self evalOutputResults];
}

- (NSArray*)updateOutputResultsWithOutlierData:(NSArray*)outlResults forCalls:(NSArray*)callList
{
	NSMutableArray *newLastcalls = [[NSMutableArray alloc] init];
	int size = [outlResults count];
	int i;
	
	for (i=0;i<size;i++) {
		if ([[outlResults objectAtIndex:i] intValue] == 2) {
			[[outputResults objectAtIndex:2] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:[[outputResults objectAtIndex:0] objectAtIndex:([[callList objectAtIndex:i] intValue]-1)]];
			[[outputResults objectAtIndex:3] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:[[outputResults objectAtIndex:1] objectAtIndex:([[callList objectAtIndex:i] intValue]-1)]];
			[[outputResults objectAtIndex:0] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:[[outputResults objectAtIndex:4] objectAtIndex:([[callList objectAtIndex:i] intValue]-1)]];
			[[outputResults objectAtIndex:1] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:[[outputResults objectAtIndex:5] objectAtIndex:([[callList objectAtIndex:i] intValue]-1)]];			
			/*[[outputResults objectAtIndex:2] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:@"Unbestimmt"];
			[[outputResults objectAtIndex:3] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:@"---"];
			[[outputResults objectAtIndex:0] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:@"Unbestimmt"];
			[[outputResults objectAtIndex:1] replaceObjectAtIndex:([[callList objectAtIndex:i] intValue]-1) withObject:@"---"];
			*/
		}
		else if ([[outlResults objectAtIndex:i] intValue] == 1) {
			[newLastcalls addObject:[callList objectAtIndex:i]];
		}
	}
	return newLastcalls;
}

- (void)evalOutputResults
{
	[managedObjectContext processPendingChanges];
	NSSortDescriptor *ccountDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"callCount" ascending:NO] autorelease];
	NSSortDescriptor *probDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"probability" ascending:NO] autorelease];
	NSMutableArray *sortDescriptor = [NSMutableArray arrayWithObjects:ccountDescriptor, probDescriptor,nil];
	[resultsTreeController setSortDescriptors:sortDescriptor];
	[resultsTreeController rearrangeObjects];
	[self getResultsFromTree];
}

- (BOOL)getResultsFromTree
{
	
	minProb = [[NSUserDefaults standardUserDefaults] floatForKey:@"mainMinProb"];;
	minCalls = [[NSUserDefaults standardUserDefaults] integerForKey:@"mainMinCalls"];

	[self writeToConsole:@"\nWalking the tree\n"];
	int i;
	int count = [[resultsTreeController arrangedRoot] count];
	[candidates release];
	candidates = [[NSMutableArray alloc] init];

	if ([[[resultsTreeController objectAtArrangedIndexPath:[NSIndexPath indexPathWithIndex:0]] valueForKey:@"callCount"] intValue] < minCalls) {
		[self writeToConsole:@"\nToo few calls, switching to single branch mode!\n"];
		minCalls = 0;
		count = 1;
	}
	
	for (i=0;i<count;i++) {
		[self walkTheTree:[NSIndexPath indexPathWithIndex:i]];
	}

	[self extractSpecies:[candidates count]>0];
}

- (BOOL)removeParentFromCandidates:(id)parent
{
	int i;
	for (i=0; i< [candidates count];i++){
		if ([candidates objectAtIndex:i] == parent) {
			[candidates removeObjectAtIndex:i];
			return YES;
		}
	}
	return NO;
}

- (void)walkTheTree:(NSIndexPath*)startHere
{
	int i;
	int count = 0;

	id currentNode = [resultsTreeController objectAtArrangedIndexPath:startHere];
	// We have to check if alltogether only less than threshold calls are available and go another route then!!!
	// PLEASE ADD THIS BEFORE DOING ANY WORK WITH ME

	if ([resultsTreeController arrangedRoot] != nil && [[currentNode valueForKey:@"callCount"] intValue] >= minCalls && [[currentNode valueForKey:@"probability"] floatValue] > minProb) {

		if ([candidates count] > 0 && [self removeParentFromCandidates:[currentNode valueForKey:@"parent"]]) [self writeToConsole:@" - parent removed - \n"];
		[candidates addObject:currentNode];

	}
	
	if ([[currentNode valueForKeyPath:@"children"] count] > 0) {
		count = [[currentNode valueForKeyPath:@"children"] count];
		for (i=0;i<count;i++) {
			if ([[[resultsTreeController objectAtArrangedIndexPath:startHere] valueForKey:@"callCount"] intValue] > 0)
				[self walkTheTree:[startHere indexPathByAddingIndex:i]];
		}
	}
}

- (void)extractSpecies:(BOOL)evalResults
{
		
	if (evalResults) {
		NSSortDescriptor *ccountDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"callCount" ascending:NO] autorelease];
		NSSortDescriptor *probDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"probability" ascending:NO] autorelease];
		NSMutableArray *sortDescriptor = [NSMutableArray arrayWithObjects:ccountDescriptor, probDescriptor,nil];
		[resultsTreeController setSortDescriptors:sortDescriptor];
		NSArray *tempArray = [candidates sortedArrayUsingDescriptors:sortDescriptor];
		
		int i;
		for (i=0; i<[tempArray count];i++)
		{
			if (i==3) {
				[self writeToConsole:@"\nMore than three valid species, only first three accounted for!\n"];
				break;
			}
			if (nil!=[[tempArray objectAtIndex:i] valueForKeyPath:@"parent"])
				[seconds addObject:[NSString stringWithFormat:@"\t%@\t%.2f\t%@\t%.2f",[[tempArray objectAtIndex:i] valueForKeyPath:@"parent.name"],[[[tempArray objectAtIndex:i] valueForKeyPath:@"parent.probability"] floatValue],[[tempArray objectAtIndex:i] valueForKey:@"name"],[[[tempArray objectAtIndex:i] valueForKey:@"probability"] floatValue]]];
			else
				[seconds addObject:[NSString stringWithFormat:@"\t\t\t%@\t%.2f",[[tempArray objectAtIndex:i] valueForKey:@"name"],[[[tempArray objectAtIndex:i] valueForKey:@"probability"] floatValue]]];	
		}
	}
	else {
		[seconds addObject:@"\t\t\tSpec.\t0.0"];
	}
	//NSLog(@"seconds %@",seconds);
}

#pragma mark -
#pragma mark Delegation and related methods

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
{
	return YES;
}

- (IBAction)deepTraversalChange:(id)sender
{
	if ([sender state] == NSOnState) {
		[(NSOpenPanel*)[sender window] setCanChooseDirectories:YES];
		//[(NSOpenPanel*)[sender window] setAllowsMultipleSelection:NO];
	}
	else {
		[(NSOpenPanel*)[sender window] setCanChooseDirectories:NO];
		//[(NSOpenPanel*)[sender window] setAllowsMultipleSelection:YES];
	}
	[[sender window] update];
}

- (void)panelSelectionDidChange:(id)sender
{
	if ([deepTraverse state] == NSOnState) [sender setCanChooseDirectories:YES];
	else [sender setCanChooseDirectories:NO];
}

/* OLD CODE FOR GETTING RESULTS

- (BOOL)getResultsFromTree
{
	int count = [[resultsTreeController arrangedRoot] count];
	[resultsTreeController rearrangeObjects];
	[managedObjectContext processPendingChanges];
	[self writeToConsole:@"\nWalking the tree\n"];
	int i;
	count = [[resultsTreeController arrangedRoot] count];
	
	if ([[outputResults objectAtIndex:2] count] < 3) tooFewCalls = YES;
	
	for (i=0;i<count;i++) {
		if ([[[resultsTreeController objectAtArrangedIndexPath:[NSIndexPath indexPathWithIndex:i]] valueForKey:@"callCount"] intValue] > 1 && !tooFewCalls)
			[self walkTheTree:[NSIndexPath indexPathWithIndex:i]];
		else if (tooFewCalls)
			[self walkTheTree:[NSIndexPath indexPathWithIndex:i]];
		else NSLog(@"Catch that #1");
	}
	if (!firstResultEntry && [winList count] > 0)
		[seconds addObject:[winList lastObject]];
	return YES;
}

- (void)walkTheTree:(NSIndexPath*)startHere
{
	int i;
	int count = 0;
	
	NSLog(@"Index is %@",startHere);
	
	id currentNode = [resultsTreeController objectAtArrangedIndexPath:startHere];
	
	if (!firstResultEntry && ([[currentNode valueForKey:@"callCount"] intValue] > 2 || [[currentNode valueForKey:@"probability"] floatValue] > 0.8)) {
		if (nil!=[currentNode valueForKeyPath:@"parent"])
			[winList addObject:[NSString stringWithFormat:@"\t%@\t%.2f\t%@\t%.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]]];
		else
			[winList addObject:[NSString stringWithFormat:@"\t\t\t%@\t%.2f",[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]]];
	}
	
	if ([[currentNode valueForKeyPath:@"children"] count] < 1) {
		if (!firstResultEntry) {
			if ([[currentNode valueForKey:@"callCount"] intValue] > 2 || [[currentNode valueForKey:@"probability"] floatValue] > 0.8) {
				//NSLog(@"Species 1 %@ - %.2f \t%@ - %.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]);
				[seconds addObject:[NSString stringWithFormat:@"\t%@\t%.2f\t%@\t%.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]]];
				firstResultEntry = YES;
			}
			else if (tooFewCalls) {
				//NSLog(@"TFC Species 1 %@ - %.2f \t%@ - %.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]);
				[seconds addObject:[NSString stringWithFormat:@"\t%@\t%.2f\t%@\t%.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]]];
				firstResultEntry = YES;	
			}
		}
		else if ([[currentNode valueForKey:@"callCount"] intValue] > 1 && [[currentNode valueForKey:@"probability"] floatValue] > 0.8) {
			//NSLog(@"Species X %@ - %.2f \t%@ - %.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]);
			[seconds addObject:[NSString stringWithFormat:@"\t%@\t%.2f\t%@\t%.2f",[currentNode valueForKeyPath:@"parent.name"],[[currentNode valueForKeyPath:@"parent.probability"] floatValue],[currentNode valueForKey:@"name"],[[currentNode valueForKey:@"probability"] floatValue]]];
		}
	}
	else {
		count = [[currentNode valueForKeyPath:@"children"] count];
		for (i=0;i<count;i++) {
			if ([[[resultsTreeController objectAtArrangedIndexPath:startHere] valueForKey:@"callCount"] intValue] > 0)
				[self walkTheTree:[startHere indexPathByAddingIndex:i]];
		}
	}
}

*/


@end
